@@ -99,6 +99,11 @@ urlpatterns += [ |
||
99 | 99 |
url(r'^wx/notify_url$', pay_views.wx_notify_url_api, name='wx_notify_url_api'), # 支付异步通知回调地址 |
100 | 100 |
] |
101 | 101 |
|
102 |
+# 提现相关 |
|
103 |
+urlpatterns += [ |
|
104 |
+ url(r'^wx/balance_withdraw$', pay_views.wx_balance_withdraw_api, name='wx_balance_withdraw_api'), # 余额提现: 企业付款/现金红包 |
|
105 |
+] |
|
106 |
+ |
|
102 | 107 |
# 分享相关 |
103 | 108 |
urlpatterns += [ |
104 | 109 |
url(r'^wx/jsapi_signature$', wechat_views.wx_jsapi_signature_api, name='wx_jsapi_signature_api'), # jsapi_signature |
@@ -261,6 +261,11 @@ WECHAT = { |
||
261 | 261 |
'appsecret': '', |
262 | 262 |
'mchID': '', |
263 | 263 |
'apiKey': '', |
264 |
+ 'mch_cert': '', |
|
265 |
+ 'mch_key': '', |
|
266 |
+ 'redpacket': { |
|
267 |
+ |
|
268 |
+ } |
|
264 | 269 |
}, |
265 | 270 |
} |
266 | 271 |
|
@@ -13,7 +13,7 @@ from account.models import LensmanIncomeExpensesInfo, LensmanInfo, UserIncomeExp |
||
13 | 13 |
from group.models import GroupPhotoInfo, GroupPhotoOrderInfo |
14 | 14 |
from pay.models import OrderInfo |
15 | 15 |
from photo.models import PhotosInfo |
16 |
-from utils.error.errno_utils import GroupPhotoStatusCode, OrderStatusCode |
|
16 |
+from utils.error.errno_utils import GroupPhotoStatusCode, OrderStatusCode, UserStatusCode, WithdrawStatusCode |
|
17 | 17 |
from utils.error.response_utils import response |
18 | 18 |
from utils.page_utils import pagination |
19 | 19 |
from utils.redis.rkeys import LENSMAN_PHOTO_PRICE |
@@ -329,3 +329,42 @@ def wx_notify_url_api(request): |
||
329 | 329 |
order_paid_success(order) |
330 | 330 |
|
331 | 331 |
return HttpResponse(settings.WXPAY_NOTIFY_SUCCESS) |
332 |
+ |
|
333 |
+ |
|
334 |
+def wx_balance_withdraw_api(request): |
|
335 |
+ user_id = request.POST.get('user_id', '') |
|
336 |
+ |
|
337 |
+ # 用户校验 |
|
338 |
+ try: |
|
339 |
+ user = UserInfo.objects.get(user_id=user_id) |
|
340 |
+ except UserInfo.DoesNotExist: |
|
341 |
+ return response(UserStatusCode.USER_NOT_FOUND) |
|
342 |
+ |
|
343 |
+ # JSAPI--公众号支付、NATIVE--原生扫码支付、APP--app支付,统一下单接口trade_type的传参可参考这里 |
|
344 |
+ trade_type = request.POST.get('trade_type', '') |
|
345 |
+ # TRANSFER--企业付款、PACKET--现金红包, 余额提现接口withdraw_type的传参可参考这里 |
|
346 |
+ withdraw_type = request.POST.get('withdraw_type', 'TRANSFER') |
|
347 |
+ amount = int(request.POST.get('amount', 0)) |
|
348 |
+ |
|
349 |
+ if user.balance < amount: |
|
350 |
+ return response(WithdrawStatusCode.BALANCE_NOT_ENOUGH) |
|
351 |
+ |
|
352 |
+ # 根据 trade_type 获取 wechat 配置 |
|
353 |
+ wechat = WECHAT.get(trade_type, {}) |
|
354 |
+ # WeChatPay 初始化 |
|
355 |
+ wxpay = WeChatPay(wechat.get('appID'), wechat.get('apiKey'), wechat.get('mchID'), mch_cert=wechat.get('mch_cert'), mch_key=wechat.get('mch_key')) |
|
356 |
+ |
|
357 |
+ if withdraw_type == 'TRANSFER': |
|
358 |
+ wxpay.transfer.transfer(user.wx_uid, amount, u'摄影师余额提现,企业付款', check_name='NO_CHECK') |
|
359 |
+ elif withdraw_type == 'PACKET': |
|
360 |
+ wxpay.redpack.send( |
|
361 |
+ user.wx_uid, |
|
362 |
+ amount, |
|
363 |
+ send_name=wechat.get('redpacket', {}).get('SEND_NAME'), |
|
364 |
+ nick_name=wechat.get('redpacket', {}).get('NICK_NAME'), |
|
365 |
+ act_name=wechat.get('redpacket', {}).get('ACT_NAME'), |
|
366 |
+ wishing=wechat.get('redpacket', {}).get('WISHING'), |
|
367 |
+ remark=wechat.get('redpacket', {}).get('REMARK'), |
|
368 |
+ ) |
|
369 |
+ |
|
370 |
+ return response(200, 'Withdraw Success', u'提现成功', {}) |
@@ -75,6 +75,11 @@ class OrderStatusCode(BaseStatusCode): |
||
75 | 75 |
NO_DETAIL_PERMISSION = StatusCodeField(404015, u'No Detail Permission', description=u'没有详情权限') |
76 | 76 |
|
77 | 77 |
|
78 |
+class WithdrawStatusCode(BaseStatusCode): |
|
79 |
+ """ 提现相关错误码 4041xx """ |
|
80 |
+ BALANCE_NOT_ENOUGH = StatusCodeField(404100, u'Balance Not Enough', description=u'提现金额不足') |
|
81 |
+ |
|
82 |
+ |
|
78 | 83 |
class MessageStatusCode(BaseStatusCode): |
79 | 84 |
""" 消息相关错误码 4090xx """ |
80 | 85 |
MESSAGE_NOT_FOUND = StatusCodeField(409001, u'Message Not Found', description=u'消息不存在') |
@@ -2,11 +2,18 @@ |
||
2 | 2 |
|
3 | 3 |
from django.http import JsonResponse |
4 | 4 |
|
5 |
+from utils.error.errno_utils import StatusCodeField |
|
5 | 6 |
|
6 |
-def response(status_code, data={}): |
|
7 |
- return JsonResponse({ |
|
7 |
+ |
|
8 |
+def response_data(status_code, message=None, description=None, data={}): |
|
9 |
+ return { |
|
8 | 10 |
'status': status_code, |
9 |
- 'message': status_code.message, |
|
10 |
- 'description': status_code.description, |
|
11 |
+ 'message': message, |
|
12 |
+ 'description': description, |
|
11 | 13 |
'data': data, |
12 |
- }) |
|
14 |
+ } |
|
15 |
+ |
|
16 |
+ |
|
17 |
+def response(status_code, message=None, description=None, data={}): |
|
18 |
+ message, description = (status_code.message, status_code.description) if isinstance(status_code, StatusCodeField) else (message, description) |
|
19 |
+ return JsonResponse(response_data(status_code, message, description, data)) |